home *** CD-ROM | disk | FTP | other *** search
/ BBS in a Box 4 / BBS in a Box - Macintosh - Volume IV (January 1992) (BBS in a Box).iso / Files / Prog / S / STACKWND.PAS < prev    next >
Encoding:
Pascal/Delphi Source File  |  1987-05-27  |  24.3 KB  |  741 lines  |  [TEXT/MACA]

  1. Program StackWindows;
  2. {
  3.    Mike asked my to add a few comments to his "MyWindow" example.  Well, I
  4.    thought MyWindow was already pretty good but I started messing around
  5.    anyway and this is what happened.  To those who may think that a lot of
  6.    this looks like it was stolen right out of MiniEdit I can only say,
  7.    "Right On".  I've even got bits of FORTH that look like MiniEdit.
  8.  
  9.    There is one departure from the user interface guidelines. Can you find
  10.    it?  Can you fix it?
  11. }
  12.  
  13. {$R-}    { Turn off range checking               }
  14. {$I-}    { Turn off I/O error checking           }
  15. {$B+}    { Turn on bundle bit }
  16. {$R StackWindow.Rsrc}
  17. {$T APPLSTAK}
  18. {$U-}
  19.  
  20. uses PasInOut,Memtypes,QuickDraw,OSIntf,ToolIntf,PackIntf;
  21.  
  22. const
  23.    AppleID     =  1000;    { IDs for our menus }
  24.    FileID      =  1001;
  25.    EditID      =  1002;
  26.    WindowID    =  1003;
  27.    Close       = 1;        { menu items }
  28.    Quit        = 3;
  29.    Undo        = 1;
  30.    Cut         = 3;
  31.    Copy        = 4;
  32.    Paste       = 5;
  33.    Clear       = 6;
  34.    Make        = 1;
  35.    Zap         = 2;
  36. {  I'm being lazy. I should make these next ones variables and get them from
  37.    WindowManagerPort.PortRect for large screens. }
  38.    ScreenWidth    = 512;
  39.    MenuBarHeight  = 20;
  40.    ScreenHeight   = 342;
  41.    Margin         = 4;
  42.    MinWidth       = 40;
  43.    MinHeight      = 40;
  44.    SBarWidth      = 16;
  45.  
  46. var
  47.    QuitIt            : boolean;
  48.    ToRight, ToBottom : integer;
  49.    theVBar,theHBar   : ControlHandle;
  50.    NextWindowNumber  : LongInt;
  51.  
  52.  
  53. { **************************************************************************** }
  54. PROCEDURE AutoScroll (theControl : ControlHandle; thePart: integer);
  55. {
  56.    Turbo loves to make static links but the ROM doesn't know squat about them.
  57.    By putting this procedure out here we can force Turbo to make a dynamic link.
  58.    AutoScroll gets called by TrackControl as long as the user is pressing the mouse
  59.    ion someplace other tham the thumb of a scroll bar.
  60. }
  61. var
  62.    delta    : integer;
  63.  
  64. begin { AutoScroll }
  65.    case thePart of
  66.       InUpButton     : delta  := -1;
  67.       InDownButton   : delta  := 1;
  68.       InPageUp       : delta  := -10;
  69.       InPageDown     : delta  := 10
  70.    end;  { case }
  71.    if thePart <> 0 then SetCtlValue(theControl,GetCtlValue(theControl) + delta)
  72. end;  { of AutoScroll }
  73.  
  74.  
  75. { **************************************************************************** }
  76. PROCEDURE InitMac;
  77. {
  78.    InitMac is designed to be a standard initialization procedure that
  79.    can be used in any application.
  80. }
  81. begin    { InitMac }
  82.    InitGraf (@ThePort);
  83.    InitFonts;
  84.    InitWindows;
  85.    InitMenus;
  86.    TEInit;
  87.    InitDialogs (Nil);   { If we were really doing a good job here we would
  88.                           pass the address of of recovery routine, rather than
  89.                           Nil, to InitDialogs.  Then, in case of a bomb, the
  90.                           resume button would run our recovery routine. }
  91.  
  92. { This next section isn't really necessary unless you're going to push
  93.   the limits of available memory. Nonetheless, I recommend that you use it
  94.   because it will keep your memory neat and may make your application run faster.
  95.   Besides if some hacker Nosys your application and sees that you know about
  96.   MoreMasters he or she will be impressed as hell. }
  97.  
  98.    MaxApplZone;
  99.    MoreMasters;
  100.    MoreMasters;
  101.    MoreMasters;
  102.    MoreMasters;
  103.  
  104.    InitCursor                    { Get a visible arrow cursor }
  105. end;  { of InitMac }
  106.  
  107. {  There is a little story I want to tell on myself here. When I first got
  108.    Mac Revealed I read Chernicoff's recommendation to use SetEventMask to
  109.    keep events that weren't of interest to the application out of the event
  110.    queue. Chernicoff had a nice little explanation about how the event queue
  111.    only had room for 20 events ect. ect.  Well it sounded pretty convincing
  112.    to me so I started putting SetEventMask calls in everything I wrote. For
  113.    the next couple of days I started having all kinds of wierd trouble
  114.    with my Mac. Not so much crashes as wierd behavior from programs that
  115.    had always worked. Finally I looked up SetEventMask in IM and found that
  116.    there were several caveats about using it.  It seems that SetEventMask
  117.    is PERMANENT ie, it's effects last untill the you restart. There are
  118.    several morals to this story:
  119.    (1) Don't use SetEventMask.
  120.    (2) If you must violate rule 1, put the system event mask back the
  121.        way you found it before exiting and before letting any DAs run.
  122.    (3) Don't believe everything you hear about Mac programming. }
  123.  
  124.  
  125. { **************************************************************************** }
  126. PROCEDURE SetUpMenus;
  127. {
  128.    Another generic procedure.  If you can make an MBAR resource with ResEdit
  129.    then this is the only code you will ever need to set up your menu bar.
  130.    ( If your app has a font menu, then do an AddResMenu to that menu also 
  131.      to add the fonts )
  132. }
  133. const
  134.    theMBar     =  1001;
  135.  
  136. var
  137.    MBarHandle     : handle;
  138.    AppleMenu      : MenuHandle;
  139.    TheError       : integer;
  140.  
  141. begin    { SetUpMenus }
  142.    MBarHandle  := GetNewMBar (theMBar);   { Gets a whole menu bar at once. }
  143.    SetMenuBar (MBarHandle);               { Make it the current menu bar. }
  144.    AppleMenu := GetMenu (AppleID);        { Get the Apple menu }
  145.    InsertMenu (AppleMenu,FileID);         { Stick it in front of File Menu }
  146.    AddResMenu (AppleMenu,'DRVR');         { Shove in DA's }
  147.    DrawMenuBar                            { show user our pretty menu }
  148. end;  { of SetUpMenus }
  149.  
  150.  
  151. { **************************************************************************** }
  152. PROCEDURE ReadEvents;
  153. {
  154.    This is what they call the main event loop.  It is the heart of a
  155.    Maclike program because it lets the user have control.
  156. }
  157. type     
  158.    ScrollHandle   =  ^ScrollPtr;
  159.    ScrollPtr      =  ^ScrollRec;
  160.    ScrollRec      =  Record
  161.                      VBar, HBar  : ControlHandle
  162.                      end;
  163. {
  164.    Take a look at the ScrollHandle structure above.  We're going to stick one into
  165.    the WindowRefCon field of each window we make.  Since it only holds a couple of
  166.    scroll bar handles, this may seem like overkill.  The point is that we can make
  167.    the record that it refers to as elaborate as we want.  We could put text edit
  168.    records, icons or whatever in the record.  This is a good place to store any
  169.    data that we want to associate with the window.  ControlRefCons can be used in
  170.    an analogous manner to associate whatever we want with a control.  BTW shouldn't
  171.    Apple have called this field a WindowRefVar?
  172. }
  173. var
  174.    theEvent       : EventRecord;
  175.  
  176.  
  177. { **************************************************************************** }
  178. PROCEDURE MoveSBars (theWindow : WindowPtr);
  179. {
  180.    This little routine moves the scroll bars to where the're supposed to be
  181.    when we open or resize a window.  Notice I overlap the scroll bar one pixel
  182.    into the window frame.  This makes prettier scroll bars.
  183. }
  184. var
  185.    therect  : rect;
  186.  
  187. begin { MoveSBars }
  188.    HideControl (theVBar);
  189.    HideControl (theHBar);
  190.    with theWindow^.PortRect do
  191.       begin
  192.          MoveControl (theVBar, right - (SBarWidth - 1), -1);
  193.          MoveControl (theHBar, -1, bottom - (SBarWidth - 1) );
  194.          SizeControl (theVBar, SBarWidth, (bottom +1) - (top - 1) - (SBarWidth - 1));
  195.          SizeControl (theHBar, (right + 1) - (left - 1) - (SBarWidth - 1), SBarWidth)
  196.       end;  { with }
  197.    ShowControl (theVBar);
  198.    ShowControl (theHBar);
  199.    ValidRect ( theHBar^^.contrlRect);  { We just drew these so don't let the }
  200.    ValidRect (theVBar^^.contrlRect)    { Update event redraw them or we'll get flicker }
  201. end;  { of MoveSBars }
  202.  
  203.  
  204. { **************************************************************************** }
  205. PROCEDURE DoBye (TheWindow : WindowPtr);
  206. {
  207.    Here is the basic kill a window routine.  Note that DisposeWindow automagically
  208.    kills the windows controls but the we made the ScrollHandle thingee so we have
  209.    to do it in ourselves to keep memory neet.
  210. }
  211. var
  212.    theHandle      : Handle;
  213.  
  214. begin { DoBye }
  215.    if TheWindow <> FrontWindow then SelectWindow (TheWindow)
  216.    else
  217.       if TrackGoAway (TheWindow, TheEvent.where ) then
  218.       begin
  219.          DisposHandle (Handle(GetWRefCon(TheWindow)));
  220.          DisposeWindow (TheWindow)
  221.       end   { then }
  222. end;  { of DoBye }
  223.  
  224.  
  225. { **************************************************************************** }
  226. PROCEDURE DoChoice (theChoice : LongInt );   { We can get here either from a menu choice or
  227.                                                from a command key alias. }
  228. var
  229.    theMenu, TheItem  : integer;
  230.  
  231.  
  232. { **************************************************************************** }
  233. PROCEDURE DoApple;   { Handle the Apple menu }
  234.  
  235. const
  236.    AboutItem   = 1;
  237.  
  238. var
  239.    DA_Name     : Str255;
  240.    DA_Number   : integer;
  241.    AppleHandle  : MenuHandle;
  242.  
  243.  
  244. { **************************************************************************** }
  245. PROCEDURE DoAbout;      { Put up our about box. }
  246.  
  247. const
  248.    AboutDialog    = 1001;
  249.  
  250. var
  251.    TheDlgPtr   : DialogPtr;
  252.    DlgItem     : integer;
  253.  
  254. begin    { DoAbout }
  255.    TheDlgPtr   := GetNewDialog (AboutDialog,Nil,WindowPtr(-1));
  256.    ModalDialog (Nil,DlgItem);    { Put up our dialog }
  257.    DisposDialog(TheDlgPtr)       { Clean up }
  258. end;  { of DoAbout }
  259.  
  260. begin    { DoApple }
  261.    if theItem = AboutItem then DoAbout else  { Either we do our about or it was a DA }
  262.    begin
  263.       AppleHandle := GetMHandle (AppleID);
  264.       GetItem (AppleHandle, theItem, DA_Name);  { We have to find out the DAs name to get it open }
  265.       DA_Number   := OpenDeskAcc (DA_Name);
  266.    end;  { else }
  267. end;  { of DoApple }
  268.  
  269.  
  270. { **************************************************************************** }
  271. PROCEDURE DoFile;
  272.  
  273. var
  274.    WhichDA     : WindowPeek;
  275.    DA_Number   : integer;
  276.  
  277. begin
  278. {
  279.    The user can only chose close from the file menu when a DA is frontmost
  280.    this routine figures out which DA was up and tells it to go bye-bye.  Of
  281.    course we could have made close kill our own windows too but in this instance
  282.    it seemed more logical to group the close and open command in their own menu.
  283. }
  284.    if theItem = Quit then QuitIt  := true
  285.    else
  286.    begin
  287.       WhichDA     := WindowPeek(FrontWindow);
  288.       DA_Number   := WhichDA^.windowKind;
  289.       CloseDeskAcc (DA_Number)
  290.    end;  { else }
  291. end;  { of Dofile }
  292.  
  293.  
  294. { **************************************************************************** }
  295. PROCEDURE DoEdit;
  296. {
  297.    We don't have anything to edit but it's nice to support miniWRITER.  Note
  298.    that if you a standard edit menu then DA can be supported by just sending
  299.    them the menu choice minus one.  If that doesn't work, it's the DA's fault.
  300.    If your application also uses the edit menu, then just put your own code
  301.    in after an "if not DA_Did then" phrase.  The OS will keep everything straight.
  302. }
  303. var
  304.    DA_Did   : Boolean;
  305.  
  306. begin
  307.    DA_Did   := SystemEdit (TheItem - 1)
  308. end;  { of DoEdit }
  309.  
  310.  
  311. { **************************************************************************** }
  312. PROCEDURE DoWindow;  { Handle a Window menu choice }
  313.  
  314. const
  315.    MakeWindow     = 1;
  316.    ZapWindow      = 2;
  317.  
  318.  
  319. { **************************************************************************** }
  320. PROCEDURE DoMake; { Make a new window }
  321.  
  322. const
  323.    WindowID    = 1001;
  324.  
  325. var
  326.    TheWindow, FromWindow   : WindowPtr;
  327.    theTitle, Count         : Str255;
  328.  
  329.  
  330. { **************************************************************************** }
  331. PROCEDURE OffSet (WhichWindow, FromWindow : WindowPtr);
  332. {
  333.    Offset the window from the one under it.  GetNewWindow has a nasty habit of
  334.    opening several identically sized windows right on top of each other so to the
  335.    poor user can't see how many windows he has open.  This is my solution and, frankly,
  336.    I'm a bit proud of it.
  337. }
  338. const
  339.    theOffset   = 20;
  340.  
  341. var
  342.    WindTopLeft : point;
  343.    
  344. begin { OffSet }
  345.    WindTopLeft := FromWindow^.PortRect.TopLeft;
  346.    LocalToGlobal (WindTopLeft);
  347.    If WindTopLeft.h >= ScreenWidth - MinWidth then ToRight := -1;
  348.    If WindTopLeft.h <= MinWidth then ToRight := 1;
  349.    If WindTopLeft.v >= ScreenHeight - MinWidth then ToBottom := -1;
  350.    If WindTopLeft.v <= MenuBarHeight + MinWidth then ToBottom := 1;
  351.    MoveWindow (WhichWindow, WindTopLeft.h + theOffset * ToRight,
  352.                             WindTopLeft.v + theOffset * ToBottom, false)
  353. end;  { of OffSet }
  354.  
  355.  
  356. { **************************************************************************** }
  357. FUNCTION DoScrolls (whichWindow : WindowPtr) : ScrollHandle;
  358. {
  359.    Returns a handle to a record containing two scroll bar handles.  Yeah, I know
  360.    that doing all this just to keep track of scroll bar handles is a bit much.
  361.    After all, the controlList field of the window record contains the handle to
  362.    the first control in a linked list of all the window's controls.  I'm doing
  363.    it this way here because you never know what kind of data structures you might
  364.    want to associate with a window.  This routine is easy to generalize.
  365. }
  366. const
  367.    ScrollID    = 1001;
  368. var
  369.    theHandle      : Handle;
  370.    Scrolls        : ScrollHandle;
  371.  
  372. begin { DoScrolls }
  373.    theHandle   := NewHandle( SizeOf (ScrollRec));
  374.    HLock (theHandle);
  375.    Scrolls  := ScrollHandle(theHandle);
  376.    with Scrolls^^ do
  377.    begin
  378.       VBar  := GetNewControl(ScrollID, whichWindow);
  379.       HBar  := GetNewControl(ScrollID, whichWindow);
  380.       theVBar  := VBar;
  381.       theHBar  := HBar
  382.    end;  { with Scrolls }
  383.    HUnLock (theHandle);
  384.    DoScrolls   := Scrolls
  385. end;  { of DoScrolls }
  386.  
  387. begin    { DoMake }
  388.    FromWindow  := FrontWindow;
  389.    TheWindow   := GetNewWindow (WindowID, Nil, WindowPtr(-1)); { Get a new window on the heap }
  390.    NextWindowNumber  := NextWindowNumber + 1;
  391.    If FromWindow <> Nil then OffSet (TheWindow, FromWindow);
  392.    GetWTitle(TheWindow,theTitle);
  393.    NumToString(NextWindowNumber,Count);
  394.    theTitle := theTitle + Count;
  395.    SetWTitle(TheWindow,theTitle);
  396.    ShowWindow (TheWindow);
  397. {  Next we get the scroll bars and store their handle in the window reference constant }
  398.    SetWRefCon (TheWindow, LongInt(DoScrolls(TheWindow)));
  399.    SetPort(TheWindow);
  400.    MoveSBars(TheWindow)
  401. end;  { of DoMake }
  402.  
  403.  
  404. { **************************************************************************** }
  405. PROCEDURE DoZap;  { Close Front Window }
  406.  
  407. begin { DoZap }
  408.    DisposHandle (Handle(GetWRefCon(FrontWindow)));
  409.    DisposeWindow (FrontWindow)
  410. end;  { of DoZap }
  411.  
  412. begin    { DoWindow }
  413.    case theItem of
  414.       MakeWindow  : DoMake;
  415.       ZapWindow   : DoZap
  416.    end;  { case }
  417. end;  { of DoWindow }
  418.  
  419. begin { DoChoice }
  420.    if theChoice <> 0 then
  421.    begin
  422.       theMenu  := HiWord (theChoice);
  423.       TheItem  := LoWord (theChoice);
  424.       case theMenu of
  425.          AppleID  : DoApple;
  426.          FileID   : DoFile;
  427.          EditID   : DoEdit;
  428.          WindowID : DoWindow
  429.       end;  { case }
  430.       HiliteMenu (0)    { UnHilite Menubar }
  431.    end;   { if }
  432. end;  { DoChoice }
  433.  
  434.  
  435. { **************************************************************************** }
  436. PROCEDURE DoMouseDown;  { Handle MouseDowns }
  437. { There are many things that a mouse click can mean to a Mac aplication.
  438.   Here we try to separate out all the important ones. }
  439.  
  440. var
  441.    whichWindow    : WindowPtr;
  442.    theClick       : integer;
  443.  
  444.  
  445. { **************************************************************************** }
  446. PROCEDURE DoMenuClick;
  447.  
  448. var
  449.    menuChoice     : LongInt;
  450.  
  451. begin    { DoMenuClick }
  452.    menuChoice  := MenuSelect (TheEvent.where);  { find out what choice the user made }
  453.    DoChoice ( menuChoice )
  454. end;  { DoMenuClick }
  455.  
  456.  
  457. { **************************************************************************** }
  458. PROCEDURE DoDrag (theWindow: WindowPtr);
  459.    
  460. var
  461.    LimitRect   : rect;
  462.  
  463. begin { DoDrag }
  464.    if theWindow = FrontWindow then
  465.       begin
  466.          SetRect (LimitRect, 0, MenuBarHeight, ScreenWidth, ScreenHeight);
  467.          InSetRect (LimitRect, Margin, Margin); { Just make sure the user can't get
  468.                                                   within 4 pixels of the edge }
  469.          DragWindow (theWindow, TheEvent.where, LimitRect)
  470.       end
  471.    else
  472.       SelectWindow(theWindow)
  473. end;  { of DoDrag }
  474.  
  475.  
  476. { **************************************************************************** }
  477. PROCEDURE DoGrow(theWindow: WindowPtr);
  478.  
  479. var
  480.    sizerect       : rect;
  481.    newSize        : LongInt;
  482.    newWidth, newHeight  : integer;
  483.  
  484. begin { DoGrow }
  485.    if theWindow <> FrontWindow then SelectWindow (theWindow )
  486.    else
  487.    begin
  488.       SetRect (sizeRect,MinWidth,MinHeight,ScreenWidth,ScreenHeight - MenubarHeight);
  489.       newSize  := GrowWindow (theWindow, TheEvent.Where, sizeRect);
  490.       if newSize <> 0 then
  491.       begin
  492.          EraseRect (theWindow^.portRect);
  493.          SizeWindow (theWindow, LoWord(NewSize), HiWord(NewSize), true );
  494.          InvalRect (theWindow^.portRect); { We don't really need this. It's casmetic }
  495.          MoveSBars (theWindow)
  496.       end   { if newSize <> 0 }
  497.    end   { else }
  498. end;  { of DoGrow }
  499.  
  500.  
  501. { **************************************************************************** }
  502. PROCEDURE DoContent (theWindow: WindowPtr);
  503. {
  504.    Content clicks can be in a control or just in the window.  We have to find
  505.    out which
  506. }
  507. var
  508.    thePoint    : point;
  509.    thePart     : integer;
  510.    theControl  : ControlHandle;
  511.  
  512.  
  513. { **************************************************************************** }
  514. PROCEDURE DoBar (theControl : ControlHandle; thePart : integer);
  515. {
  516.    Note: You have to handle thumb clicks differently than every other kind!
  517. }
  518. begin   { DoBar }
  519.    if thePart = InThumb then thePart   := TrackControl(theControl, thePoint, Nil)
  520.    else
  521.    thePart   := TrackControl(theControl, thePoint, @AutoScroll)
  522. end;    { of DoBar }
  523.  
  524.  
  525. { **************************************************************************** }
  526. PROCEDURE DoStampIcon(thePoint : point);
  527. {
  528.    Compile this and see this one for yourself.
  529. }
  530. const
  531.    FaceIcon    = 1001;
  532.  
  533. var
  534.    theIcon     : Handle;
  535.    PlotRect    : rect;
  536.  
  537. begin { DoStampIcon }
  538.    theIcon  := GetIcon(FaceIcon);
  539.    SetRect(PlotRect, thePoint.h - 15, thePoint.v - 15, thePoint.h +16, thePoint.v + 16);
  540.    PlotIcon(PlotRect,theIcon)
  541. end;  { of DoStampIcon }
  542.  
  543. begin { DoContent }
  544.    If theWindow <> FrontWindow then
  545.       SelectWindow (theWindow)
  546.    else
  547.       begin
  548.          thePoint := TheEvent.where;
  549.          GlobalToLocal(thePoint);
  550.          thePart  := FindControl (thePoint, theWindow, theControl);
  551.          if theControl <> Nil then
  552.             DoBar (theControl, thePart)
  553.          else DoStampIcon(thePoint);
  554.       end   { else }
  555. end;  { of DoContent }
  556.  
  557.  
  558. { **************************************************************************** }
  559. PROCEDURE DoZoom (TheWindow : WindowPtr; TheClick: integer);
  560. {
  561.    Zoom Windows!  Hope you have the new ROM.  With the 64K ROM this code
  562.    never gets called.
  563. }
  564. begin { DoZoom }
  565.    if TrackBox (TheWindow, TheEvent.where, TheClick) then
  566.    begin
  567.       EraseRect(TheWindow^.PortRect);
  568.       ZoomWindow (TheWindow, TheClick, true);
  569.       MoveSBars(TheWindow)
  570.    end   { if }
  571. end;  { of DoZoom }
  572.  
  573. begin { DoMouseDown }
  574.    theClick := FindWindow (TheEvent.where,WhichWindow);
  575.    case theClick of
  576.       InMenuBar      : DoMenuClick;
  577.       InSysWindow    : SystemClick(TheEvent, whichWindow);  { Pass the event on
  578.                                                               to the DA }
  579.       InDrag         : DoDrag (WhichWindow);
  580.       InGrow         : DoGrow (WhichWindow);
  581.       InGoAway       : DoBye (WhichWindow);
  582.       InContent      : DoContent(WhichWindow);
  583.       InZoomIn, InZoomOut  :  DoZoom (WhichWindow,theClick)
  584.    end;  { case }
  585. end;  { of DoMouseDown }
  586.  
  587.  
  588. { **************************************************************************** }
  589. PROCEDURE DoKeyDown;    { Our KeyDown routine only checks for menu aliases }
  590. {
  591.    What can I say about this one?  If Bill Atkinson had written the Event Manager
  592.    I'm sure we could do something nice and Pascalish.  But it needed to fit in 64K
  593.    so they gave the job to Andy and he wrote it in assembler.  When you try to do
  594.    down and dirty assembler type things from Pascal, the Pascal can get wierd.
  595.    Let me just say that this code checks the event record to see if the command key
  596.    was down.  If it wasn't the code does nothing. If it was, the code looks at
  597.    the event record again to find out what other key was also down, calls the menu
  598.    manager to see what that key is an alias for and then calls DoChoice as if the
  599.    user had actually made a menu selection.  Quite a bit for one line!
  600. }
  601. begin { DoKeyDown }
  602.    if (BitAnd (TheEvent.modifiers, CmdKey) <> 0) then   { Do nothing if command key wasn't down. }
  603.       DoChoice (MenuKey(CHR(BitAnd(TheEvent.message,CharCodeMask))));
  604. end;  { of DoKeyDown }
  605.  
  606.  
  607. { **************************************************************************** }
  608. PROCEDURE DoUpdate;
  609. {
  610.    Take care of Update Events.  Another generic routine.
  611. }
  612.  
  613. var
  614.    SavePort    : GrafPtr;
  615.    theWindow   : WindowPtr;
  616.  
  617. begin { DoUpdate }
  618.    GetPort (SavePort);
  619.    theWindow   := WindowPtr (TheEvent.message);
  620.    SetPort (theWindow);
  621.    BeginUpDate (theWindow);
  622.       DrawGrowIcon(theWindow);
  623.       DrawControls (theWindow);
  624.    EndUpDate (theWindow);
  625.    SetPort (SavePort)
  626. end;  { of DoUpdate }
  627.  
  628.  
  629. { **************************************************************************** }
  630. PROCEDURE DoActivate;
  631. {
  632.    Note how we set the global variables theVBar & theHBar.  The rest of this
  633.    program can just use the globals and know that they refer to the controls
  634.    for the right window.
  635. }
  636. const
  637.    active      = 0;
  638.    inactive    = 255;
  639. var
  640.    theWindow   : WindowPtr;
  641.    theBars     : ScrollHandle;
  642.    theHandle   : handle;
  643.  
  644. begin { DoActivate }
  645.    theWindow   := WindowPtr(theEvent.message);
  646.    SetPort (theWindow);
  647.    thehandle   := handle(GetWRefCon(theWindow));
  648.    HLock(thehandle);
  649.    theBars  := ScrollHandle (thehandle);
  650.    theVBar  := theBars^^.VBar;
  651.    theHBar  := theBars^^.HBar;
  652.    HUnLock(theHandle);
  653.    if BitAnd (TheEvent.modifiers, ActiveFlag) <> 0
  654.    then
  655.       begin
  656.          HiliteControl (theVBar, active);
  657.          HiliteControl (theHBar, active)
  658.       end   { if }
  659.    else
  660.       begin
  661.          HiliteControl (theVBar, inactive);
  662.          HiliteControl (theHBar, inactive)
  663.       end;  { else }
  664.    DrawGrowIcon(theWindow)
  665. end;  { of DoActivate }
  666.  
  667. begin    { ReadEvents }
  668.    if GetNextEvent(EveryEvent,theEvent) then
  669.    case theEvent.what of
  670.       MouseDown         : DoMouseDown;
  671.       KeyDown           : DoKeyDown;
  672.       UpDateEvt         : DoUpdate;
  673.       ActivateEvt       : DoActivate
  674.    end   { case }    { the OS can handle such events as disk insertion without help }
  675. end;  { of ReadEvents }
  676.  
  677.  
  678. { **************************************************************************** }
  679. PROCEDURE CheckMenus;
  680. {
  681.    Turn on Edit & Close Menus for DA's.  A lot of people are always screwing around
  682.    with their menus depending on the users choices.  I like to do all my menu
  683.    didling in one routine and put that routine in the main event loop.
  684. }
  685. var
  686.    FileHandle, EditHandle, AppleHandle, WindHandle  : MenuHandle;
  687.    theFront    : WindowPeek;
  688.    DAs        : boolean;
  689.  
  690. begin { CheckMenus }
  691.    WindHandle  := GetMHandle (WindowID);
  692.    FileHandle  := GetMHandle (FileID);
  693.    EditHandle  := GetMHandle (EditID);
  694.    AppleHandle := GetMHandle (AppleID);
  695.    theFront    := WindowPeek (FrontWindow);
  696.    DAs         := false;
  697.    if (theFront <> Nil) then if (theFront^.Windowkind <> UserKind) then DAs := true;
  698.    { ie. if there's a window and it's not ours it must be a DA's. }
  699.    If DAs then
  700.    begin
  701.       EnableItem (FileHandle,Close);
  702.       EnableItem (EditHandle,Cut);
  703.       EnableItem (EditHandle,Copy);
  704.       EnableItem (EditHandle,Paste);
  705.       EnableItem (EditHandle,Clear);
  706.       EnableItem (EditHandle,Undo)
  707.    end   { if }
  708.    else
  709.    begin
  710.       DisableItem (FileHandle,Close);  { but if it's ours, turn em off }
  711.       DisableItem (EditHandle,Cut);
  712.       DisableItem (EditHandle,Copy);
  713.       DisableItem (EditHandle,Paste);
  714.       DisableItem (EditHandle,Clear);
  715.       DisableItem (EditHandle,Undo)
  716.    end;  { else }
  717.    if (theFront <> Nil) and not DAs then EnableItem (WindHandle,Zap )
  718.    else DisableItem (WindHandle,Zap ); { ie only enable Zap if there is one 
  719.                                         of our windows to zap. }
  720.    { This next call checks to see if there is a least eneough memory for
  721.      one more window.  If there isn't, we disable the menu choice. }
  722.    if (CompactMem (SizeOf (WindowRecord)) >= SizeOf (WindowRecord))
  723.    then EnableItem (WindHandle, Make) else DisableItem (WindHandle, Make)
  724. end;  { of CheckMenus }
  725.  
  726. {  Isn't it nice to have a nice clean main routine?  I can't understand
  727.    why anyone would want to use more than what's below. }
  728.  
  729. begin { StackWindows }
  730.    InitMac;
  731.    QuitIt  := False;
  732.    NextWindowNumber  := 0;
  733.    SetUpMenus;
  734.    ToRight  := 1;
  735.    ToBottom := 1;
  736.    repeat
  737.       ReadEvents;
  738.       SystemTask;    { Give the DA's a shot. }
  739.       CheckMenus
  740.    Until QuitIt = true
  741. end.  { of StackWindows }